//-------------------------------------------------------------------
//	Copyright (c) 2013  Zhirnov Andrey
//	This file is part of the "Noise Library" project.
//	See copyright notice in "linNoiseCL.h".
//-------------------------------------------------------------------

#include "CContext.h"

// programs //
#include "../Program/CProgramCL.h"
#include "../Program/CProgramNative.h"

// sources //
#include "../Source/Math/CSourceConst.h"
#include "../Source/Math/CMathSource1Arg.h"
#include "../Source/Math/CMathSource2Args.h"
#include "../Source/Math/CMathSource3Args.h"
#include "../Source/Controls/CSourceOutput.h"
#include "../Source/Controls/CSourceGradient.h"
#include "../Source/Controls/CSourceSwizzle.h"
#include "../Source/Generators/CSourceBillow.h"
#include "../Source/Generators/CSourceCheckerboard.h"
#include "../Source/Generators/CSourceCurve.h"
#include "../Source/Generators/CSourceCylinders.h"
#include "../Source/Generators/CSourcePerlin.h"
#include "../Source/Generators/CSourceRidgedMulti.h"
#include "../Source/Generators/CSourceSelect.h"
#include "../Source/Generators/CSourceSpheres.h"
#include "../Source/Generators/CSourceTerrace.h"
#include "../Source/Generators/CSourceTurbulence.h"
#include "../Source/Generators/CSourceVoronoi.h"
#include "../Source/Transforms/CSourceDisplace.h"
#include "../Source/Transforms/CSourceScalePoint.h"
#include "../Source/Transforms/CSourceTranslatePoint.h"
#include "../Source/Transforms/CSourceRotatePoint.h"


namespace Noise
{
	
	// constructor
	CContext::CContext () : _isInited(false)
	{
		WINDOWS_ONLY(
		_logger.Create(	e_log_output::MESSAGE_BIT
					  | e_log_output::LOG_FILE_BIT
					  , "log.html" );
		)
	}

	// destructor
	CContext::~CContext ()
	{
	}

	// Create
	EResult CContext::Create (EContextFlags flags)
	{
		Delete();

		CHECK_RETURN_R( _device.Init( "", cl_device_type::GPU ), INVALID_OPERATION );
		CHECK_RETURN_R( _queue.Create( cl_queue_property::NONE ), INVALID_OPERATION );

		if ( EnumCmp( flags, SUPPORT_IMAGE_VIEWER ) )
		{
			CHECK_RETURN_R( _imageViewer.Create(), INVALID_OPERATION );
		}

		_Init();
		return OK;
	}

	// CreateCL
	EResult CContext::CreateCL (cl_platform_id platformId, cl_device_id deviceId, cl_context context)
	{
		Delete();

		CHECK_RETURN_R( _device.Init( platformId, deviceId, context ), INVALID_OPERATION );
		CHECK_RETURN_R( _queue.Create( cl_queue_property::NONE ), INVALID_OPERATION );

		_Init();
		return OK;
	}
	
	// CreateGL
	EResult CContext::CreateGL (void *dc, void *rc, EContextFlags flags)
	{
		Delete();
		
		CHECK_RETURN_R( _device.Init( "", cl_device_type::GPU, rc, dc ), INVALID_OPERATION );
		CHECK_RETURN_R( _queue.Create( cl_queue_property::NONE ), INVALID_OPERATION );
		
		if ( EnumCmp( flags, SUPPORT_IMAGE_VIEWER ) )
		{
			CHECK_RETURN_R( _imageViewer.Create( rc ), INVALID_OPERATION );
		}

		_Init();
		return OK;
	}
	
	// CreateDX
	EResult CContext::CreateDX (void *dx, EContextFlags flags)
	{
		return _NO_IMPLEMENTATION;

		Delete();
		
		//CHECK_RETURN_R( _device.Init( "", cl_device_type::GPU, rc, dc ), INVALID_OPERATION );
		//CHECK_RETURN_R( _queue.Create( cl_queue_property::NONE ), INVALID_OPERATION );

		_Init();
		return OK;
	}
	
	// _Init
	void CContext::_Init ()
	{
		WINDOWS_ONLY(
		string		s_info;
		TSystemInfo	s_sys_info;

		TPlatform::GetSystemInfo( s_sys_info );
		TPlatform::GetSystemInfoString( s_sys_info, s_info );
		_device.GetOpenCLInfo( s_info );
		_device.GetOpenCLParams( s_info );
		s_info << "---------------";
		LOG( s_info.cstr(), e_log_msg::INFO );
		)

		_device.GetMaxSizeOfImage3D( _maxImageSize );


		// register programs
		CHECK_WARNING( RegisterProgram( &CProgramCL::CreateProgram, OPENCL_PROGRAM ) );
		
		NATIVE(
		CHECK_WARNING( RegisterProgram( &CProgramNative::CreateProgram, NATIVE_PROGRAM ) );
		)

		// register sources
		CHECK_WARNING( RegisterSource( &CSourceConst::CreateSource,		CONSTANT ) );

		CHECK_WARNING( RegisterSource( &CSourceAbs::CreateSource,		ABS ) );
		CHECK_WARNING( RegisterSource( &CSourceInv::CreateSource,		INV ) );
		CHECK_WARNING( RegisterSource( &CSourceExp::CreateSource,		EXP ) );
		CHECK_WARNING( RegisterSource( &CSourceExp2::CreateSource,		EXP2 ) );
		CHECK_WARNING( RegisterSource( &CSourceExp10::CreateSource,		EXP10 ) );
		CHECK_WARNING( RegisterSource( &CSourceLog::CreateSource,		LOG ) );
		CHECK_WARNING( RegisterSource( &CSourceLog2::CreateSource,		LOG2 ) );
		CHECK_WARNING( RegisterSource( &CSourceLog10::CreateSource,		LOG10 ) );
		CHECK_WARNING( RegisterSource( &CSourceScaleBias::CreateSource,	SCALE_BIAS ) );

		CHECK_WARNING( RegisterSource( &CSourceAdd::CreateSource,		ADD ) );
		CHECK_WARNING( RegisterSource( &CSourceDiv::CreateSource,		DIV ) );
		CHECK_WARNING( RegisterSource( &CSourceMax::CreateSource,		MAX ) );
		CHECK_WARNING( RegisterSource( &CSourceMin::CreateSource,		MIN ) );
		CHECK_WARNING( RegisterSource( &CSourceMod::CreateSource,		MOD ) );
		CHECK_WARNING( RegisterSource( &CSourceMul::CreateSource,		MUL ) );
		CHECK_WARNING( RegisterSource( &CSourceNormExp::CreateSource,	NORM_EXP ) );
		CHECK_WARNING( RegisterSource( &CSourcePow::CreateSource,		POW ) );
		CHECK_WARNING( RegisterSource( &CSourceSub::CreateSource,		SUB ) );

		CHECK_WARNING( RegisterSource( &CSourceClamp::CreateSource,		CLAMP ) );
		CHECK_WARNING( RegisterSource( &CSourceMad::CreateSource,		MAD ) );
		CHECK_WARNING( RegisterSource( &CSourceMix::CreateSource,		MIX ) );

		CHECK_WARNING( RegisterSource( &CSourceOutput::CreateSource,	RENDER_OUTPUT ) );
		CHECK_WARNING( RegisterSource( &CSourceGradient::CreateSource,	GRADIENT ) );
		CHECK_WARNING( RegisterSource( &CSourceSwizzle::CreateSource,	SWIZZLE ) );

		CHECK_WARNING( RegisterSource( &CSourceBillow::CreateSource,		BILLOW ) );
		CHECK_WARNING( RegisterSource( &CSourceCheckerboard::CreateSource,	CHECKERBOARD ) );
		CHECK_WARNING( RegisterSource( &CSourceCurve::CreateSource,			CURVE ) );
		CHECK_WARNING( RegisterSource( &CSourceCylinders::CreateSource,		CYLINDERS ) );
		CHECK_WARNING( RegisterSource( &CSourcePerlin::CreateSource,		PERLIN ) );
		CHECK_WARNING( RegisterSource( &CSourceRidgedMulti::CreateSource,	RIDGED_MULTI ) );
		CHECK_WARNING( RegisterSource( &CSourceSelect::CreateSource,		SELECT ) );
		CHECK_WARNING( RegisterSource( &CSourceTerrace::CreateSource,		TERRACE ) );
		CHECK_WARNING( RegisterSource( &CSourceTurbulence::CreateSource,	TURBULENCE ) );
		CHECK_WARNING( RegisterSource( &CSourceVoronoi::CreateSource,		VORONOI ) );

		CHECK_WARNING( RegisterSource( &CSourceDisplace::CreateSource,		DISPLACE ) );
		CHECK_WARNING( RegisterSource( &CSourceScalePoint::CreateSource,	SCALE_POINT ) );
		CHECK_WARNING( RegisterSource( &CSourceTranslatePoint::CreateSource, TRANSLATE_POINT ) );
		CHECK_WARNING( RegisterSource( &CSourceRotatePoint::CreateSource,	ROTATE_POINT ) );

		_isInited = true;
	}

	// Delete
	void CContext::Delete ()
	{
		_imageViewer.Destroy();
		_sources.Clear();
		_programs.Clear();
		_device.Destroy();

		_maxImageSize	= uvec3();
		_flags			= EContextFlags(0);
		_isInited		= false;
	}
	
	// SetInteger
	EResult CContext::SetInteger (EContextParameter pname, const int *v, int count)
	{
		CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

		switch ( pname )
		{
			case IMAGE_VIEWER_SIZES :
				CHECK_ARGUMENT_R( count >= 2, INVALID_VALUE );
				_imageViewer.SetSize( uvec2( v[0], v[1] ) );
				return OK;

			case IMAGE_VIEWER_FLAGS :
				_imageViewer.SetFlags( (EImageViewerFlags)v[0] );
				return OK;
		}
		return INVALID_ENUM;
	}

	// GetInteger
	EResult CContext::GetInteger (EContextQuery pname, int *v, int count)
	{
		CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

		switch ( pname )
		{
			case CONTEXT_VERSION :
				v[0] = LIB_NOISE_CL_VERSION;
				return OK;

			case CONTEXT_CREATED :
				v[0] = _isInited;
				return OK;

			case CONTEXT_FLOAT_POINT_TYPE_SIZE :
				v[0] = sizeof(real) << 3;
				return OK;
				
			case CONTEXT_SHARED_WITH_GL :
				v[0] = _device.IsSharedWithOpenGL();
				return OK;

			case CONTEXT_SHARED_WITH_DX :
				v[0] = _device.IsSharedWithDirectX();
				return OK;

			case CONTEXT_SUPPORTED_SOURCES_COUNT :
				v[0] = _srcCtors.Count();
				return OK;

			case CONTEXT_SUPPORTED_SOURCES :
				CHECK_ARGUMENT_R( (uint)count >= _srcCtors.Count(), INVALID_VALUE );
				FOR( i, _srcCtors ) {
					v[i] = _srcCtors[i].first;
				}
				return OK;

			case CONTEXT_SUPPORTED_PROGRAMS_COUNT :
				v[0] = _progCtors.Count();
				return OK;

			case CONTEXT_SUPPORTED_PROGRAMS :
				CHECK_ARGUMENT_R( (uint)count >= _progCtors.Count(), INVALID_VALUE );
				FOR( i, _progCtors ) {
					v[i] = _progCtors[i].first;
				}
				return OK;
		};
		return INVALID_ENUM;
	}

	// CreateSource
	CSourcePtr CContext::CreateSource (ESource type)
	{
		src_ctors_t::cpair_t *	pair = null;
		CSourcePtr				src  = null;

		CHECK_RETURN_R( _srcCtors.Find( type, pair ), null );
		CHECK_RETURN_R( pair->second.Call( src, this ), null );

		_sources.PushBack( src );
		return src;
	}
	
	// CreateSourceCopy
	CSourcePtr CContext::CreateSourceCopy (CSourcePtr src)
	{
		CSourcePtr res = src->Clone();

		_sources.PushBack( res );
		return res;
	}

	// CreateProgram
	CProgramPtr CContext::CreateProgram (EProgram type)
	{
		prog_ctors_t::cpair_t *	pair = null;
		CProgramPtr				prog = null;

		CHECK_RETURN_R( _progCtors.Find( type, pair ), null );
		CHECK_RETURN_R( pair->second.Call( prog, this ), null );

		_programs.PushBack( prog );
		return prog;
	}
	
	// DeleteSource
	void CContext::DeleteSource (CSourcePtr src)
	{
		_sources.FindAndFastErase( src );
		src->Delete();
	}

	// DeleteProgram
	void CContext::DeleteProgram (CProgramPtr prog)
	{
		_programs.FindAndFastErase( prog );
		prog->Delete();
	}

	// RegisterSource
	bool CContext::RegisterSource (PCreateSource_t proc, ESource type)
	{
		_srcCtors.Add( type, SourceType( proc, type ) );
		return true;
	}

	// UnregisterSource
	bool CContext::UnregisterSource (ESource type)
	{
		_srcCtors.Erase( type );
		return true;
	}

	// RegisterProgram
	bool CContext::RegisterProgram (PCreateProgram_t proc, EProgram type)
	{
		_progCtors.Add( type, ProgramType( proc, type ) );
		return true;
	}

	// UnregisterProgram
	bool CContext::UnregisterProgram (EProgram type)
	{
		_progCtors.Erase( type );
		return true;
	}

}	// Noise

#include <stl_core/ux_stl_core_lib.h>

#ifdef _NOISE_CROSS_PLATFORM

#	include <stl_ext/CL/opencl.cpp>
#	include <stl_ext/CL/shell/CLDevice.cpp>
#	include <stl_ext/CL/shell/CLQueue.cpp>
#	include <stl_ext/CL/shell/CLEvent.cpp>
#	include <stl_ext/CL/shell/CLProgram.cpp>
#	include <stl_ext/CL/shell/CLKernel.cpp>
#	include <stl_ext/CL/shell/CLMemory.cpp>
#	include <stl_ext/CL/shell/CLSampler.cpp>

#	include <stl_ext/OS/Windows/SyncPrimitives.cpp>
#	include <stl_ext/Types/TypesData.cpp>

#else

#	include <filesystem/ux_filesystem_lib.h>
//#	include <ocf_parser/ux_ocf_parser_lib.h>
#	include <stl_ext/ux_stl_ext_lib.h>

#endif
